//Random unit test for the Adventurer card
//Adventurer effects:	Continue to draw cards until you obtain 2 treasure cards. Discard all other drawn cards.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dominion.h"
#include "dominion_helpers.h"
#include "rngs.h"

typedef struct gameState G;

//Loc struct holds location information about adventurer card and treasure cards
typedef struct Loc{
	int adv;
	int t1;
	int t2;
	int allInDeck;
}Loc;

void random_gamestate(G *game, Loc *loc);
void pop(int *stack, int count);
int testPassed(G *g1, G *g2, Loc *loc);
void print_results(int passed, int total);

int main(int argc, char *argv[]) {
	int i;
	int seed, num_tests;
	int test_passed;
	G before, after;
	Loc loc;

	//get command line input
	if (argc != 3){
		printf("Usage: randomtestadventurer [seed] [number of tests]");
		return(0);
	}
	seed = atoi(argv[1]);
	num_tests = atoi(argv[2]);
	test_passed = 0;

	//seed the random generator
	SelectStream(1);
	PutSeed((long)seed);

	//test loop
	for (i = 0; i<num_tests; i++){
		//get a random gamestate
		random_gamestate(&before, &loc);

		//create a copy
		memcpy(&after, &before, sizeof(G));

		//play the card
		cardEffect(adventurer, 0, 0, 0, &after, loc.adv, 0);

		//test results
		if (testPassed(&before, &after, &loc)){
			test_passed++;
		}
	}

	//print the results
	print_results(test_passed, num_tests);

	return 0;
}



//********************************************************
//Creates a random gamestate for testing
//Param: 	G *game - pointer to game struct to populate
//Param:	Loc *loc - pointer to location struct
//Param: 	int seed - seed to generate random numbers
//Return: 	void
//********************************************************
void random_gamestate(G *game, Loc *loc){
	int i;
	int r_player, r_handcount, r_deckcount, r_discardcount;
	int treasure1, treasure2;
	int totalCards;

	//set gamestate values to zero
	memset(game, 0, sizeof(G));

	//generate random inputs
	game->whoseTurn = r_player = (int)(Random() * (MAX_PLAYERS - 1));
	game->handCount[r_player] = r_handcount = (int)(Random() * (100 - 5)) + 5; //5-100 cards in hand
	totalCards = (int)(Random() * (400 - 10)) + 10;
	game->discardCount[r_player] = r_discardcount = (int)(Random() * totalCards); //0 - totalCards in discard
	game->deckCount[r_player] = r_deckcount = totalCards - r_discardcount; //0 - totalCards in deck

	//populate game with dummy cards
	pop(game->hand[r_player], r_handcount);
	pop(game->discard[r_player], r_discardcount);
	pop(game->deck[r_player], r_discardcount);

	//add the adventurer card to the hand
	loc->adv = (int)(Random() * (r_handcount - 1));
	game->hand[r_player][loc->adv] = adventurer;

	//create two random treasure cards
	treasure1 = (int)(Random() * 2) + copper;
	treasure2 = (int)(Random() * 2) + copper;

	//10% of the time make sure the deck is empty
	if ((int)(Random() * 9) == 0 || r_deckcount == 0){
		//change counts
		game->deckCount[r_player] = r_deckcount = 0;
		game->discardCount[r_player] = r_discardcount + r_deckcount;
		r_discardcount += r_deckcount;

		//repopulate decks
		pop(game->discard[r_player], r_discardcount);
		pop(game->deck[r_player], r_discardcount);

		//add treasure card to discard pile
		loc->allInDeck = 0;
		loc->t1 = (int)(Random() * (r_discardcount - 1));
		game->discard[r_player][loc->t1] = treasure1;

		//add one more card to the discard pile in a different spot
		do{
			loc->t2 = (int)(Random() * (r_discardcount - 1));
		} while (game->discard[r_player][loc->t2] == treasure1);
		game->discard[r_player][loc->t2] = treasure2;
	}
	//deck has at least 1 card
	else{
		//10% of the time split the treasure cards between deck and discard
		if ((r_discardcount > 0 && (int)(Random() * 9) == 0) || r_deckcount == 1){
			loc->allInDeck = 0;
			loc->t1 = (int)(Random() * (r_discardcount - 1));
			game->discard[r_player][loc->t1] = treasure1;

			//put second treasure card in the deck
			else{
				loc->t2 = (int)(Random() * (r_deckcount - 1));
				game->deck[r_player][loc->t2] = treasure2;
			}
		}
		//put both in deck
		else{
			loc->allInDeck = 1;
			//if deck only has 2 cards, don't make the positions random
			if (r_deckcount == 2){
				loc->t1 = 0;
				loc->t2 = 1;
				game->deck[r_player][loc->t1] = treasure1;
				game->deck[r_player][loc->t2] = treasure2;
			}
			//put treasure cards in random position
			else{
				//add first treasure card to the deck
				loc->t1 = (int)(Random() * (r_deckcount - 1));
				game->deck[r_player][loc->t1] = treasure1;

				//add one more card to the deck in a different spot
				do{
					loc->t2 = (int)(Random() * (r_deckcount - 1));
				} while (game->deck[r_player][loc->t2] == treasure1);
				game->deck[r_player][loc->t2] = treasure2;
			}
		}
	}
}



//********************************************************
//Populates a stack of cards with dummy cards
//Param: 	int *stack - pointer to the stack of cards we'll populate
//Param:	int count - the size of the stack
//Return: 	void
//********************************************************
void pop(int *stack, int count){
	int i;
	for (i = 0; i<count; i++){
		stack[i] = 100;
	}
}



//********************************************************
//Compares two gamestates
//Param: 	G *g1 - pointer to the 'before' gamestate
//Param:	G *g2 - pointer to the 'after' gamestate
//Param:	Loc *loc - pointer to the location info struct
//Return: 	1 if tests passed, 0 if test failed
//********************************************************
int testPassed(G *g1, G *g2, Loc *loc){
	int i, p, card, result, cardCount, drawnCount;
	p = g2->whoseTurn;
	result = 1;

	//Hand should have 2 treasure cards
	for (i = 0; i < g2->handCount[p]; i++){
		card = g2->hand[p][i];
		if (card == copper || card == silver || card == gold) cardCount++;
	}
	if (cardCount != 2){
		printf("Error: %d treasure cards in hand instead of 2!\n", cardCount);
		result = 0;
	}

	//Adventurer card should no longer be in our hand
	for (i = 0; i < g2->handCount[p]; i++){
		card = g2->hand[p][i];
		if (card == adventurer){
			printf("Error: still have the adventurer card in our hand!\n");
			result = 0;
			break;
		}
	}

	//Adventurer card should be in the played card pile
	cardCount = 0;
	for (i = 0; i<g2->playedCardCount; i++){
		card = g2->playedCards[i];
		if (card == adventurer) cardCount++;
	}
	if (cardCount != 1){
		printf("Error: %d adventurer cards in played card pile instead of 1!\n", cardCount);
		result = 0;
	}

	//Hand should have 1 additional card (+2 treasure, -1 adv)
	if (g1->handCount[p] + 1 != g2->handCount[p]){
		printf("Error: %d cards in hand instead of %d!\n", g2->handCount[p], g1->handCount[p] + 1);
		result = 0;
	}

	//if both treasure cards were in the deck
	if (loc->allInDeck){

		//figure out how many cards were pulled from the deck
		if (loc->t1 < loc->t2){
			drawnCount = (g1->deckCount[p] - 2) - loc->t1;
		}
		else{
			drawnCount = (g1->deckCount[p] - 2) - loc->t2;
		}

		//Deck should be missing 'drawnCount' cards
		if (g1->deckCount[p] - drawnCount != g2->deckCount[p]){
			printf("Error: %d cards in deck instead of %d!\n", g2->handCount[p], g1->handCount[p] - drawnCount);
			result = 0;
		}

		//Discard pile should have 'drawnCount' - 2 cards (Total drawn -2 treasure)
		if (g1->discardCount[p] + (drawnCount - 2) != g2->discardCount[p]){
			printf("Error: %d cards in discard pile instead of %d!\n", g2->discardCount[p], g1->discardCount[p] + (drawnCount - 2));
			result = 0;
		}

	}

	//return the result of the tests
	return result;
}



//********************************************************
//Print the results of testing
//Param: 	int passed - number of tests that passed
//Param:	int total - total number of tests run
//Return: 	void
//********************************************************
void print_results(int passed, int total){
	double results = ((double)passed) / total;
	printf("---------------------\n");
	printf("%.f%% of test cases passed (%d/%d)\n", (results * 100), passed, total);
}
